#ifndef _Serialize_H
#define _Serialize_H

#include <vector>
#include <assert.h>
#include <string.h>
#include <string>
#include "FileArchive.h"
#include "EArray.h"

struct CStringList;
struct CClassMapBase;
enum EType : unsigned char{
	type_none,
	type_proba,
	type_test,
	type_proba4
};

enum SerializeState : unsigned char{
	state_ok,
	state_serialize,
	state_deserialize,
	state_error
};

class EDataBuffer;
namespace SerializeNS{

	
	
	struct CSerializer{
		static CFileArchive* s_Archive;
		
		CSerializer(const char* SaveName);
		CStringList*  _Strings;
		CFileHandler* _Stream;
		EDataBuffer*  _DataBuffers;

		CSerializer& operator & (){ return *this; }
		virtual CSerializer& update(){ return *this; }
	};
	struct Serialize : public CSerializer{
		Serialize(const char* SaveName);
		virtual bool and(){ return true; }
	};
	struct Deserialize : public CSerializer{
		Deserialize(const char* SaveName);
		virtual bool and(){ return true; }
	};


	inline size_t getVarOffset(const void* BaseAddress, const void* VarAddres){
		size_t a = 0, b = 0;
		memmove(&a, &BaseAddress, sizeof(size_t));
		memmove(&b, &VarAddres, sizeof(size_t));
		return b - a;
	}
	inline char* getVarAddress(const void* BaseAddress, size_t Offset){
		char* a;
		memmove(&a, &BaseAddress, sizeof(char*));
		return (a + Offset);
	}

	struct VarInfo{
		VarInfo(const char* Name, size_t Offset, size_t Size) :_Name(Name), _Offset(Offset), _Size(Size){}
		const std::string _Name;
		size_t   _Offset;
		size_t   _Size;
		unsigned short _NameIndex;
		virtual bool serialize(void* StartAddress, EDataBuffer* Buffer);
	};

	struct VarInfoClass : public VarInfo{
		VarInfoClass(const char* Name, size_t Offset, size_t Size) : VarInfo(Name, Offset, Size){}
		virtual bool serialize(void* StartAddress, EDataBuffer* Buffer);;
	};

	struct VarInfoClassPointer : public VarInfo{
		VarInfoClassPointer(const char* Name, size_t Offset, size_t Size) : VarInfo(Name, Offset, Size){}
		virtual bool serialize(void* StartAddress, EDataBuffer* Buffer);;
	};

	struct VarInfoString : public VarInfo{
		VarInfoString(const char* Name, size_t Offset, size_t Size) : VarInfo(Name, Offset, Size){}
		virtual bool serialize(void* StartAddress, EDataBuffer* Buffer);;
	};

	struct CClassMapBase{
		CClassMapBase() : _Type(type_none), _Base(0), _Buffer(0){
			//_Classes.push_back(this);
		}
		EType _Type;
		CClassMapBase*  _Base;
		EArray<VarInfo*> _Vars;
		EArray<size_t>  _IdVars;
		EDataBuffer*    _Buffer;
		virtual void* getInstance(size_t wParam, size_t lParam){
			return 0;
		}
		virtual bool doSerialize(void* ClassAddress, CSerializer* Buffer){
			return true;
		}
	};

	template<class T>
	struct CClassMap : public CClassMapBase{
		CClassMap() : CClassMapBase(){}
		virtual void* getInstance(size_t wParam, size_t lParam){
			return T::getInstance(wParam, lParam);
		}
		virtual bool doSerialize(void* ClassAddress, CSerializer* Buffer){
			return ((T*)ClassAddress)->doSerialize(Buffer);
		}
	};

	

	class Base{
	public:
		Base(){}
		Base(EType t){}

		EType _Type;

		inline EType getType(){
			return _Type;
		}
		void  start(const char* SaveFileName, SerializeState State);
		static 
		void* getInstance(size_t wParam, size_t lParam){ return 0; }

		bool  doSerialize(CSerializer* Buffer){ return 0; }

		bool  serialize(CSerializer* Buffer);

		/* create once class info for this type */
		template<class TDeliverClass, class TBaseClass>
		bool createClassInfo(TDeliverClass DeliverClassAddress, TBaseClass BaseClassAddress, EType Type){
			static CClassMapBase* ClassInfo     = getClassInfo(*DeliverClassAddress);
			static CClassMapBase* BaseClassInfo = getClassInfo(*BaseClassAddress);
			static bool SetType = setClassType(ClassInfo, Type);
			static bool SetBase = setClassBase(ClassInfo, BaseClassInfo);
			return true;
		}

		template <class T>
		bool addSerializableVar(T ClassAddres, const char*& Var, const char* VarName, bool IsId){
			static CClassMapBase* ClassInfo = getClassInfo(*ClassAddres);
			ClassInfo->_Vars.add(new VarInfoString(VarName, getVarOffset(ClassAddres, &Var), sizeof(const char*)));
			if (IsId) ClassInfo->_IdVars.add(ClassInfo->_Vars.count() - 1);
			return true;
		}
		template <class T>
		bool addSerializableVar(T ClassAddres, const Base& Var, const char* VarName, bool IsId){
			static CClassMapBase* ClassInfo = getClassInfo(*ClassAddres);
			ClassInfo->_Vars.add(new VarInfoClass(VarName, getVarOffset(ClassAddres, &Var), 0));
			if (IsId) ClassInfo->_IdVars.add(ClassInfo->_Vars.count() - 1);
			return true;
		}
		template <class T>
		bool addSerializableVar(T ClassAddres, const Base*& Var, const char* VarName, bool IsId){
			static CClassMapBase* ClassInfo = getClassInfo(*ClassAddres);
			ClassInfo->_Vars.add(new VarInfoClassPointer(VarName, getVarOffset(ClassAddres, &Var), sizeof(Base*)));
			if (IsId) ClassInfo->_IdVars.add(ClassInfo->_Vars.size() - 1);
			return true;
		}
#ifdef addVar
#define __addVar__backup__ addVar
#undef addVar
#endif

#define addVar(var_type) template <class T> bool addSerializableVar(T ClassAddres, var_type & Var, const char* VarName, bool IsId){ \
	static CClassMapBase* ClassInfo = getClassInfo(*ClassAddres); \
	ClassInfo->_Vars.add(new VarInfo(VarName, getVarOffset(ClassAddres, &Var), sizeof(var_type))); \
		if (IsId) ClassInfo->_IdVars.add(ClassInfo->_Vars.count() - 1); return true; }

		addVar(const bool);
		addVar(const char);  addVar(const unsigned char);
		addVar(const short); addVar(const unsigned short);
		addVar(const int);   addVar(const unsigned int);
		addVar(const long);  addVar(const unsigned long);
		addVar(const float); addVar(const double);
		
#undef addVar

#ifdef __addVar__backup__
#define addVar __addVar__backup__
#undef __addVar__backup__
#endif
	private:
		
		bool setClassType(CClassMapBase* ClassInfo, EType Type);
		bool setClassBase(CClassMapBase* ClassInfo, CClassMapBase* BaseClassInfo);

		template <class T>
		CClassMapBase* getClassInfo(T& ClassReference){
			static CClassMapBase* ClassInfo = new CClassMap<T>();
			return ClassInfo;
		}
	};
}
typedef SerializeNS::Base Serialize;


#ifndef SERIALIZE
#define SERIALIZE_INIT(original_type) bool original_type##_serializable_var = createClassInfo(this, (original_type*) this, Type);
#define SERIALIZE(value)      static bool value##_serializable_var = addSerializableVar(this, value, #value, false); value
#define SERIALIZE_ID(value)   static bool value##_serializable_var = addSerializableVar(this, value, #value, true ); value
#endif

#endif /* _Serialize_H */